package com.plc.demo.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * PLC数据服务 - 处理MQTT数据下发和接收
 */
@Service
public class PlcDataService {

    private static final Logger logger = LoggerFactory.getLogger(PlcDataService.class);

    @Autowired
    private MqttConnectOptions mqttConnectOptions;

    @Value("${spring.mqtt.url}")
    private String brokerUrl;

    @Value("${spring.mqtt.clientid}")
    private String clientIdPrefix;

    @Autowired
    private AddressService addressService;

    private MqttClient mqttClient;
    private final ObjectMapper objectMapper = new ObjectMapper();

    // 存储UUID和对应的目标值
    private final Map<String, Double> uuidTargetMap = new ConcurrentHashMap<>();
    private final Map<String, Boolean> PRINTMAP = new ConcurrentHashMap<>();

    public HashMap<String, Object> markedMap = new HashMap<>();
    // 存储UUID对应的接收数据
    private final Map<String, Map<String, Number>> uuidDataMap = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() throws MqttException {
        String clientId = clientIdPrefix + UUID.randomUUID().toString().replace("-", "").substring(0, 8);
        mqttClient = new MqttClient(brokerUrl, clientId, new MemoryPersistence());
        mqttClient.setCallback(new MqttCallback() {
            @Override
            public void connectionLost(Throwable cause) {
                logger.error("MQTT connection lost", cause);
                try {
                    reconnect();
                } catch (MqttException e) {
                    logger.error("Failed to reconnect", e);
                }
            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                handleMessage(topic, message);
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {
                logger.debug("Message delivered: {}", token.getMessageId());
            }
        });

        reconnect();
    }

    private void reconnect() throws MqttException {
        if (!mqttClient.isConnected()) {
            mqttClient.connect(mqttConnectOptions);
            logger.info("MQTT client connected: {}", mqttClient.getClientId());
        }
    }

    @PreDestroy
    public void destroy() throws MqttException {
        if (mqttClient != null && mqttClient.isConnected()) {
            mqttClient.disconnect();
            mqttClient.close();
            logger.info("MQTT client disconnected");
        }
    }

    /**
     * 下发数据读取请求
     *
     * @param devId       设备ID
     * @param addressType 地址类型，如 "VW"
     * @param startValue  起始值
     * @param endValue    终止值
     * @param increment   增量
     * @param targetValue 目标值（用于标记接近的值）
     * @return UUID
     */
    public List<String> sendReadRequest(String devId, String addressType, int startValue, int endValue,
                                        int increment, double targetValue, Double diff) {
        // 生成地址列表
        List<String> addresses = addressService.generateAddresses(addressType, startValue, endValue, increment);
        return sendReadRequestWithAddresses(devId, addresses, targetValue, diff,true);
    }

    /**
     * 读取开关地址（主地址0~31，子地址0~7）
     *
     * @param devId 设备ID
     * @return UUID列表
     */
    public List<String> sendSwitchReadRequest(String devId, String switchAddressType) {
        List<String> switchAddresses = addressService.generateSwitchAddresses(switchAddressType);
        return sendReadRequestWithAddresses(devId, switchAddresses, 1.0, null, false);
    }

    private List<String> sendReadRequestWithAddresses(String devId, List<String> addresses,
                                                      Double targetValue, Double diff, Boolean print) {
        List<String> uuidList = new ArrayList<>();

//        // 按50个地址分组
//        List<List<String>> batches = addressService.batchAddresses(addresses, 50);
//
//        for (List<String> batch : batches) {
        String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 8);
        uuidList.add(uuid);
        // 订阅接收主题（在下发前订阅）
        String subscribeTopic = "HMI/11005/" + devId + "/read/" + uuid;
        if (null != targetValue) {
            uuidTargetMap.put(uuid, targetValue);
        }
        PRINTMAP.put(uuid, print);
        try {
            if (!mqttClient.isConnected()) {
                reconnect();
            }
            mqttClient.subscribe(subscribeTopic, 1);
            logger.info("Subscribed to topic: {}", subscribeTopic);
        } catch (MqttException e) {
            logger.error("Failed to subscribe to topic: {}", subscribeTopic, e);
            throw new RuntimeException("Failed to subscribe", e);
        }

        // 下发数据（每50个地址一次）
        String publishTopic = "HPI/11005/" + devId + "/read/" + uuid;
        try {
            String addressString = addressService.addressesToFormattedString(addresses);
            Map<String, String> requestData = new HashMap<>();
            requestData.put("addres", addressString);

            String jsonPayload = objectMapper.writeValueAsString(requestData);

            MqttMessage message = new MqttMessage(jsonPayload.getBytes());
            message.setQos(1);

            if (!mqttClient.isConnected()) {
                reconnect();
            }

            mqttClient.publish(publishTopic, message);
            logger.info("Published to {}: {}", publishTopic, jsonPayload);

        } catch (Exception e) {
            logger.error("Failed to publish message", e);
        }
        try {
            Thread.sleep(2000); // 等待接收
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
        //    }

        return uuidList;
    }

    /**
     * 处理接收到的消息
     */
    private void handleMessage(String topic, MqttMessage message) {
        try {
            String payload = new String(message.getPayload());
            logger.info("Received message from topic {}: {}", topic, payload);

            // 解析主题获取UUID
            String uuid = extractUuidFromTopic(topic);
            if (uuid == null || payload.isEmpty() || payload.indexOf("不存在") > -1) {
                logger.warn("Cannot extract UUID from topic: {}", topic);
                return;
            }

            // 解析JSON数据
            Map<String, Number> dataMap = objectMapper.readValue(
                    payload,
                    new TypeReference<Map<String, Number>>() {
                    }
            );

            // 存储数据
            uuidDataMap.put(uuid, dataMap);

            // 获取目标值
            Double targetValue = uuidTargetMap.get(uuid);
            boolean ispring = PRINTMAP.getOrDefault(uuid,false);

            // 打印并标记数据
            printAndMarkData(uuid, dataMap, targetValue,ispring);

        } catch (Exception e) {
            logger.error("Error handling message from topic: {}", topic, e);
        }
    }

    /**
     * 从主题中提取UUID
     */
    private String extractUuidFromTopic(String topic) {
        // 主题格式: HMI/11005/{devid}/read/{UUID}
        String[] parts = topic.split("/");
        if (parts.length >= 5 && "read".equals(parts[3])) {
            return parts[4];
        }
        return null;
    }

    /**
     * 打印数据并标记接近目标值的项
     */
    private void printAndMarkData(String uuid, Map<String, Number> dataMap, Double targetValue, boolean isprint) {
        if (dataMap == null || dataMap.isEmpty()) {
            logger.info("UUID: {} - No data received", uuid);
            return;
        }
        logger.info("========== UUID: {} ==========", uuid);
        logger.info("Received data:");
        HashMap<String, Object> mmp = new HashMap<>();
        double tolerance = 20000; // 容差值，可以根据需要调整
        for (Map.Entry<String, Number> entry : dataMap.entrySet()) {
            String address = entry.getKey();
            Number value = entry.getValue();
            double doubleValue = value.doubleValue();

            if (isprint) {
                logger.info("  {}: {}", address, value);
            }

            // 检查是否接近目标值  -- (Math.abs(doubleValue - targetValue) <= tolerance)
            if (targetValue != null && doubleValue >= targetValue) {
                markedMap.put(address, value);
                mmp.put(address, value);
//                logger.warn("  *** MARKED: {} = {} (接近目标值 {}) ***",
//                        address, value, targetValue);
            }
        }

        if (!mmp.isEmpty()) {
            logger.info("========== 标记项总数: {} ==========", mmp.size());
            for (String key : mmp.keySet()) {
                logger.warn("  *** {} = {} ***", key, dataMap.get(key));
            }
        }

        logger.info("====================================");
    }

    /**
     * 获取指定UUID的接收数据
     */
    public Map<String, Number> getReceivedData(String uuid) {
        return uuidDataMap.get(uuid);
    }

    /**
     * 获取所有UUID的接收数据
     */
    public Map<String, Map<String, Number>> getAllReceivedData() {
        return new HashMap<>(uuidDataMap);
    }

    /**
     * 在多个设备中轮流下发地址数据
     *
     * @param addressList 地址列表，格式为 "类型 地址" 或直接是地址字符串
     * @param targetValue 目标值（用于标记接近的值）
     * @param diff        差值
     * @return 所有生成的UUID列表
     */
    public List<String> sendReadRequestToMultipleDevices(List<String> addressList, double targetValue, Double diff) {
        // 定义设备列表：1100504, 1100506~1100518, 1100588
        List<String> deviceList = new ArrayList<>();
        //  deviceList.add("1100504");
        for (int i = 1100504; i <= 1100518; i += 2) {
            deviceList.add(String.valueOf(i));
        }
        deviceList.add("1100588");

        List<String> allUuids = new ArrayList<>();

        // 将地址列表按设备数量分组，轮流分配给各个设备
        int deviceCount = deviceList.size();
        int deviceIndex = 0;

//        // 将地址列表分组（每组可以包含多个地址）
//        List<List<String>> addressBatches = new ArrayList<>();
//        for (int i = 0; i < addressList.size(); i += 4) { // 每次处理4个地址，可以根据需要调整
//            int end = Math.min(i + 4, addressList.size());
//            addressBatches.add(addressList.subList(i, end));
//        }

        // 轮流分配给各个设备
        for (String devId : deviceList) {
            //  String devId = deviceList.get(deviceIndex % deviceCount);
            //  deviceIndex++;

            // 为这批地址生成UUID
            String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 8);
            allUuids.add(uuid);

            // 订阅接收主题
            String subscribeTopic = "HMI/11005/" + devId + "/read/" + uuid;
            uuidTargetMap.put(uuid, targetValue);

            try {
                if (!mqttClient.isConnected()) {
                    reconnect();
                }
                mqttClient.subscribe(subscribeTopic, 1);
                logger.info("Subscribed to topic: {}", subscribeTopic);
            } catch (MqttException e) {
                logger.error("Failed to subscribe to topic: {}", subscribeTopic, e);
                continue;
            }

            // 下发数据
            String publishTopic = "HPI/11005/" + devId + "/read/" + uuid;
            try {
                // 格式化地址字符串：地址类型 地址1,地址2,地址3...
                String addressString = addressService.addressesToFormattedString(addressList);

                Map<String, String> requestData = new HashMap<>();
                requestData.put("addres", addressString);

                String jsonPayload = objectMapper.writeValueAsString(requestData);

                MqttMessage message = new MqttMessage(jsonPayload.getBytes());
                message.setQos(1);

                if (!mqttClient.isConnected()) {
                    reconnect();
                }

                mqttClient.publish(publishTopic, message);
                logger.info("Published to {}: {}", publishTopic, jsonPayload);

            } catch (Exception e) {
                logger.error("Failed to publish message to device: {}", devId, e);
            }

            // 等待一段时间再发送下一个
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                logger.error("Thread interrupted", e);
            }
        }

        return allUuids;
    }

    /**
     * 在多个设备中轮流下发地址数据（通过地址参数生成）
     *
     * @param addressType 地址类型，如 "VD"
     * @param startValue  起始值
     * @param endValue    终止值
     * @param increment   增量
     * @param targetValue 目标值
     * @param diff        差值
     * @return 所有生成的UUID列表
     */
    public List<String> sendReadRequestToMultipleDevices(String addressType, int startValue, int endValue,
                                                         int increment, double targetValue, Double diff) {
        // 生成地址列表
        List<String> addresses = addressService.generateAddresses(addressType, startValue, endValue, increment);

        // 调用多设备下发方法
        return sendReadRequestToMultipleDevices(addresses, targetValue, diff);
    }
}

